// 发布订阅模式
// 使用递归
// 回调异步执行
// 使用了闭包作用域
// then 函数返回的是提供了默认构造函数的promise
// 执行顺序  用户resolve ,用户then 函数传递的回调， then 函数返回的promise 的 resolve ， 下一个then 函数接收到的 回调函数

const PENDING = "PENDING";
const RESOLVE = "RESOLVE";
const REJECT = "REJECT";

const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) {
    return reject(new TypeError("不能引用同一个对象"));
  }

  if ((typeof x === "object" && x != null) || typeof x === "function") {
    if (x.constructor === Promise && x.then !== undefined) {
      x.then(
        (data) => {
          resolvePromise(promise2, data, resolve, reject);
        },
        (err) => {
          reject(err);
        }
      );
    } else {
      resolve(x);
    }
  } else {
    resolve(x);
  }
};

class Promise {
  constructor(executor) {
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;

    let resolve = (value) => {
      if (this.status === PENDING) {
        // 如果resolve了一个promise进来，则等promise 获取了结果，再改变状态，触发回调
        if (typeof value === "object" && value.constructor === Promise) {
          value.then(
            (data) => {
              resolve(data);
            },
            (err) => {
              resolve(err);
            }
          );
          return;
        }
        this.value = value;
        this.status = RESOLVE;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

    let reject = (reason) => {
      if (this.status === PENDING) {
        // 如果reason是一个promise对象，则等promise 获取了结果，再改变状态，触发回调
        if (typeof reason === "object" && reason.constructor === Promise) {
          reason.then(
            (data) => {
              reject(data);
            },
            (err) => {
              reject(err);
            }
          );
          return;
        }
        this.reason = reason;
        this.status = REJECT;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  then(onResolved, onRejected) {
    // 设置默认函数
    onResolved = typeof onResolved === "function" ? onResolved : (v) => v;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    // 这个promise 是系统默认构建的，用户是无法给这个promise 传递构造函数的
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVE) {
        // 因为使用了异步执行，Promise 的构造函数中调用 executor 捕获错误捕获不了
        setTimeout(() => {
          try {
            let x = onResolved(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECT) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onResolved(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
  catch(errCallback) {
    return this.then(null, errCallback);
  }

  finally(onFinally) {
    return new Promise((resolve, reject) => {
      this.then(
        (result) => {
          onFinally();
          resolve(result);
        },
        (reason) => {
          onFinally();
          reject(reason);
        }
      );
    });
  }
}

// 返回的结果数组和传进来的promise 数组的元素下标要对应上
Promise.all = function (arr) {
  let length = arr.length;
  let index = 0;
  let res = [];
  return new Promise((resolve, reject) => {
    for (let i = 0; i < length; i++) {
      Promise.resolve(arr[i]).then(
        (data) => {
          res[i] = data;
          index++;
          if (index === length) {
            resolve(res);
          }
        },
        (err) => {
          reject(err);
        }
      );
    }
  });
};

Promise.race = function (arr) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < arr.length; i++) {
      Promise.resolve(arr[i]).then(
        (data) => {
          resolve(data);
        },
        (err) => {
          reject(err);
        }
      );
    }
  });
};

Promise.resolve = function (res) {
  return new Promise((resolve) => resolve(res));
};

Promise.reject = function (reason) {
  return new Promise((resolve, reject) => reject(reason));
};

Promise.allSettled = function (arr) {
  let length = arr.length;
  let index = 0;
  let res = [];
  return new Promise((resolve, reject) => {
    for (let i = 0; i < length; i++) {
      Promise.resolve(arr[i]).then(
        (data) => {
          res[i] = { status: "fulfilled", value: data };
          index++;
          if (index === length) {
            resolve(res);
          }
        },
        (reason) => {
          res[i] = { status: "rejected", reason };
          index++;
          if (index === length) {
            resolve(res);
          }
        }
      );
    }
  });
};

module.exports = Promise;
